/*******************************************************************************
 * Copyright (c) Jonathan Buck - jonbuck16@gmail.com
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    jonbuck16@gmail.com - initial API and implementation
 *******************************************************************************/
package net.jonbuck.tassoo.ui.dialog;

import net.jonbuck.tassoo.model.Category;
import net.jonbuck.tassoo.model.Priority;
import net.jonbuck.tassoo.model.ReferenceModel;
import net.jonbuck.tassoo.model.Status;
import net.jonbuck.tassoo.model.Statuses;
import net.jonbuck.tassoo.model.Type;
import net.jonbuck.tassoo.ui.nl.Messages;
import net.jonbuck.tassoo.ui.util.CommonImages;

import org.apache.commons.lang.StringUtils;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.ValidationStatusProvider;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.validation.IValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.databinding.dialog.TitleAreaDialogSupport;
import org.eclipse.jface.databinding.dialog.ValidationMessageProvider;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;

/**
 * 
 * 
 * @since 1.0.0
 */
public class ReferenceModelDialog extends TitleAreaDialog {

	private Spinner associatedPercentage;

	private DataBindingContext bindingContext;

	private Text categoryName;

	private boolean isDefault;

	private boolean isEdit;

	private Button makeDefault;

	private String message;

	private ReferenceModel model;

	private Composite newDefaultComposite;

	private String originalValue;

	private Object[] possibleValues;

	private Combo pvCombo;

	private Statuses statuses;

	private String title;

	private String type;

	/**
	 * 
	 * @param parentShell
	 */
	public ReferenceModelDialog(Shell parentShell, ReferenceModel model, String title, String message, String type) {
		super(parentShell);
		this.model = model;
		if (this.model.isDefault()) {
			isDefault = true;
		}
		this.title = title;
		this.message = message;
		this.type = type;
	}

	/**
	 * <p>
	 * <b>Creates a new instance of the <code>EditReferenceModel</code> dialog
	 * for edit mode.</b> <br/>
	 * 
	 * </p>
	 * 
	 * @param parentShell
	 *            the shell to use when constructing the dialog.
	 */
	public ReferenceModelDialog(Shell parentShell, ReferenceModel model, String title, String message, String type,
			Object[] possibleValues) {
		super(parentShell);
		this.model = model;
		if (this.model.isDefault()) {
			isDefault = true;
			originalValue = this.model.getValue();
		}
		this.title = title;
		this.message = message;
		this.type = type;
		this.possibleValues = possibleValues;
		this.isEdit = true;
	}

	/**
	 * 
	 */
	public void create() {
		super.create();
		setTitle(title);
		setMessage(message);
		if (this.model instanceof Category) {
			setTitleImage(CommonImages.getImage(CommonImages.WIZBAN_CATEGORY));
		} else if (this.model instanceof Priority) {
			setTitleImage(CommonImages.getImage(CommonImages.WIZBAN_PRIORITY));
		} else if (this.model instanceof Status) {
			setTitleImage(CommonImages.getImage(CommonImages.WIZBAN_STATUS));
		} else if (this.model instanceof Type) {
			setTitleImage(CommonImages.getImage(CommonImages.WIZBAN_TYPE));
		}
	}

	/**
	 * <p>
	 * <b>Creates the contents of the dialog.</b>
	 * </p>
	 * 
	 * @param parent
	 *            the composite within which to add the dialogs controls.
	 */
	protected Control createDialogArea(final Composite parent) {
		final Composite outerContainer = (Composite) super.createDialogArea(parent);

		Composite container = new Composite(outerContainer, SWT.NONE);
		GridLayout containerLayout = new GridLayout(2, false);
		containerLayout.marginTop = containerLayout.marginBottom = containerLayout.marginLeft = containerLayout.marginRight = 15;
		containerLayout.verticalSpacing = 15;
		container.setLayout(containerLayout);
		container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

		initializeDialogUnits(container);

		Label label = new Label(container, SWT.NONE);
		label.setText(Messages.referencemodeldialog_label_name);

		categoryName = new Text(container, SWT.BORDER);
		categoryName.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

		if (this.model instanceof Status) {
			Label associatedPercentageLabel = new Label(container, SWT.NONE);
			associatedPercentageLabel.setText(Messages.referencemodeldialog_label_associatedpercentage);

			associatedPercentage = new Spinner(container, SWT.BORDER);
			associatedPercentage.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
			associatedPercentage.setIncrement(5);
			associatedPercentage.setMinimum(0);
			associatedPercentage.setMaximum(100);
		}

		makeDefault = new Button(container, SWT.CHECK);
		makeDefault.setAlignment(SWT.RIGHT);
		makeDefault.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 2, 1));
		makeDefault.setText(Messages.referencemodeldialog_button_makedefault);
		makeDefault.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				if (isEdit && isDefault) {
					if (makeDefault.getSelection() == false) {
						int count = 0;
						String[] items = new String[possibleValues.length - 1];
						for (Object possible : possibleValues) {
							ReferenceModel possibleModel = (ReferenceModel) possible;
							if (!possibleModel.getValue().equals(originalValue)) {
								items[count] = possibleModel.getValue();
								count++;
							}
						}
						pvCombo.setItems(items);
						pvCombo.setText(items[0]);
						newDefaultComposite.setVisible(true);
						((GridData) newDefaultComposite.getLayoutData()).exclude = false;
					} else {
						newDefaultComposite.setVisible(false);
						((GridData) newDefaultComposite.getLayoutData()).exclude = true;
					}
					outerContainer.layout(true);
					getShell().setSize(getInitialSize());
				}
			}
		});
		// We need to disable this control if the item is being edited and is
		// the only item in the list.
		if (this.isEdit && possibleValues != null && possibleValues.length == 1) {
			makeDefault.setEnabled(false);
		}

		newDefaultComposite = new Composite(container, SWT.NONE);
		GridLayout gl_newDefaultComposite = new GridLayout(2, false);
		gl_newDefaultComposite.marginLeft = 0;
		gl_newDefaultComposite.marginRight = 0;
		gl_newDefaultComposite.marginWidth = 0;
		newDefaultComposite.setLayout(gl_newDefaultComposite);
		newDefaultComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));

		Label lblNewDefault = new Label(newDefaultComposite, SWT.NONE);
		lblNewDefault.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
		lblNewDefault.setText(Messages.bind(Messages.editreferencemodeldialog_newdefault_text, type));

		pvCombo = new Combo(newDefaultComposite, SWT.NONE);
		pvCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

		newDefaultComposite.setVisible(false);
		((GridData) newDefaultComposite.getLayoutData()).exclude = true;

		// Build the separator line
		Label titleBarSeparator = new Label(parent, SWT.HORIZONTAL | SWT.SEPARATOR);
		titleBarSeparator.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

		initDataBindings();
		initBindingSupport();

		return outerContainer;
	}

	/**
	 * 
	 * @return
	 */
	public ReferenceModel getModel() {
		return model;
	}

	/**
	 * <p>
	 * <b>Initialises the binding support for the dialog.</b><br/>
	 * This method initialises a <code>TitleAreaDialogSupport</code> class which
	 * links an errors that are created from the data binding code to error
	 * messages that are displayed in the title area of the dialog. In order to
	 * manage the enablement of the OK button we need to subclass the validation
	 * message provider so that we can add code that either enables or disables
	 * the button based on whether there are any errors.
	 * </p>
	 */
	private void initBindingSupport() {
		TitleAreaDialogSupport tads = TitleAreaDialogSupport.create(this, bindingContext);
		tads.setValidationMessageProvider(new ValidationMessageProvider() {
			public String getMessage(ValidationStatusProvider statusProvider) {
				if (statusProvider == null) {
					return message;
				}
				return super.getMessage(statusProvider);
			}

			public int getMessageType(ValidationStatusProvider statusProvider) {
				int type = super.getMessageType(statusProvider);
				if (getButton(IDialogConstants.OK_ID) != null) {
					getButton(IDialogConstants.OK_ID).setEnabled(type != IMessageProvider.ERROR);
				}
				return type;
			}
		});
	}

	/**
	 * 
	 * @return
	 */
	protected void initDataBindings() {
		bindingContext = new DataBindingContext();

		IObservableValue categoryNameObserveTextObserveWidget = SWTObservables.observeText(categoryName, SWT.Modify);
		IObservableValue categorygetValueEmptyObserveValue = PojoObservables.observeValue(model, "value");
		UpdateValueStrategy nameStrategy = new UpdateValueStrategy();
		nameStrategy.setBeforeSetValidator(new IValidator() {
			public IStatus validate(Object value) {
				if (StringUtils.isBlank(categoryName.getText())) {
					String modelType = type;
					return ValidationStatus.error(Messages.bind(Messages.editreferencemodeldialog_error_name_text, modelType));
				}
				return ValidationStatus.ok();
			}
		});
		bindingContext.bindValue(categoryNameObserveTextObserveWidget, categorygetValueEmptyObserveValue, nameStrategy, null);

		if (this.model instanceof Status) {
			IObservableValue associatedPercentageObserveWidget = SWTObservables.observeSelection(associatedPercentage);
			IObservableValue associatedPercentageObserveValue = PojoObservables.observeValue(model, "associatedPercentage");
			UpdateValueStrategy apTTMStrategy = new UpdateValueStrategy();
			apTTMStrategy.setBeforeSetValidator(new IValidator() {
				public IStatus validate(Object value) {
					int percentageValue = (Integer) value;
					for (Status status : statuses.getStatus()) {
						if (!model.equals(status)) {
							if (status.getAssociatedPercentage() == percentageValue) {
								return ValidationStatus
										.error(Messages.editreferencemodeldialog_error_associatedpercentage_percentagetaken);
							}
						}
					}
					return ValidationStatus.ok();
				}
			});
			bindingContext.bindValue(associatedPercentageObserveWidget, associatedPercentageObserveValue, apTTMStrategy, null);
		}

		IObservableValue defaultObserveSelectionWidget = SWTObservables.observeSelection(makeDefault);
		IObservableValue isDefaultValueEmptyObserveValue = PojoObservables.observeValue(model, "default");
		bindingContext.bindValue(defaultObserveSelectionWidget, isDefaultValueEmptyObserveValue, null, null);

		IObservableValue pvComboObserveTextObserveWidget = SWTObservables.observeSelection(pvCombo);
		IObservableValue modelNewValueObserveValue = PojoObservables.observeValue(model, "newValue");
		bindingContext.bindValue(pvComboObserveTextObserveWidget, modelNewValueObserveValue, null, null);

	}

	/**
	 * 
	 * @return
	 */
	public boolean isDefault() {
		return isDefault;
	}

	/**
	 * 
	 * @param category
	 */
	public void setModel(ReferenceModel model) {
		this.model = model;
	}

	/**
	 * @param statuses
	 *            the statuses to set
	 */
	public void setStatuses(Statuses statuses) {
		this.statuses = statuses;
	}
}
